/*
 * BAT_GeneID_Validator.c
 *
 * Validates the gene id of gene tables by merging similar transcripts.
 *
 * $Id: BAT_GeneID_Validator.c 931 2006-09-05 09:42:04Z rpz $
 *
 * Bob Zimmermann <rpz@cse.wustl.edu> Totally free.
 *
 */

#include "BAT_GeneID_Validator.h"

BAT_Validate_Plugin BAT_GeneID_Validate_Plugin = {
  BAT_GeneID_Validator_Type,
  "GeneID Validator",
  "geneid",
  "Validates geneIDs by merging similar transcripts",
  "geneid [ OPTIONS ]\n" "   -e, --enable    enable the plugin\n"
    "   -d, --disable   disable the plugin (default)\n"
    "   --cds-only      merge genes with cds similar only (default: no)\n"
    "   --do-not-delete merge gene ids but do not delete similar ones (default: no)\n",
  BAT_GeneID_Validator_new,
  BAT_GeneID_Validator_delete,
  BAT_GeneID_Validator_cmd_parse,
  BAT_GeneID_Validator_validate_gene_table,
  NULL,
  NULL,
  NULL
};

void *
BAT_GeneID_Validator_new()
{
  BAT_GeneID_Validate_Context *vc;
  vc = malloc(sizeof(BAT_GeneID_Validate_Context));
  vc->enabled = 0;
  vc->match_all_features = 1;
  vc->dont_delete = 1;
  return vc;
}

void
BAT_GeneID_Validator_delete(void *p, int befrei)
{
  if (befrei)
    free(p);
}


/* XXX hsould this be global?? sounds bad. */
static int gid_v_enabled = 0, gid_v_match_all_features = 1, gid_v_dont_delete =
  0;
static struct option gid_v_opts[] = {
  {"enable", no_argument, &gid_v_enabled, 1},
  {"disable", no_argument, &gid_v_enabled, 0},
  {"cds-only", no_argument, &gid_v_match_all_features, 1},
  {"do-not-delete", no_argument, &gid_v_dont_delete, 1},
  {0, 0, 0, 0}
};

int
BAT_GeneID_Validator_cmd_parse(void *p, int argc, char **argv)
{
  BAT_GeneID_Validate_Context *vc = p;
  int li, c;
  char *last_arg;


  last_arg = argv[1];
  while ((c = getopt_long(argc, argv, "ed", gid_v_opts, &li)) >= 0) {
    switch (c) {
    case 0:
      break;
    case 'e':
      gid_v_enabled = 1;
      break;
    case 'd':
      gid_v_enabled = 0;
      break;
    default:
      return 1;
    }
    last_arg = argv[1];
  }

  vc->enabled = gid_v_enabled;
  vc->match_all_features = gid_v_match_all_features;
  vc->dont_delete = gid_v_dont_delete;

  return 0;
}

BAT_GeneTab *
BAT_GeneID_Validator_validate_gene_table(void *p, BAT_GeneTab * gt)
{
  BAT_GeneID_Validate_Context *vc = (BAT_GeneID_Validate_Context *) p;

  BAT_Feature *feat, *f1, *f2, **feats;
  da_pos_t n_fs;
  BAT_Coord f, i, j, start;

  partition_t *ggs;
  darray_t *free_feats;
  BAT_Free_Feature *ffeat, **ffeats;

  BAT_Builder *b;

  /* get the features, sorted by coordinate */
  feats = BAT_GeneTab_get_features(gt, &n_fs);

  /* now we begin creating a partition of these features */
  ggs = partition_new(n_fs, NULL);
  for (f = 0; f < n_fs; f++)
    partition_add(ggs, feats[f]);

  /*
   * get sets of overlapping features [start, f) and do pairwise
   * comparisons of them.  If they match, put their transcripts into
   * partitions
   */

  for (f = 1; f < n_fs; f++) {
    start = f - 1;
    do {
      f1 = partition_get(ggs, f - 1);
      f2 = partition_get(ggs, f);
      f++;
    }
    while (f < n_fs && f1->end >= f2->start);
    /* no joining needs to be done to a lone feature */
    if (start <= f - 2)
      continue;
    for (i = start; i < f - 1; i++) {
      for (j = start + 1; j < f; j++) {
	if (i == j)
	  continue;
	/*
	 * link the transcripts which match the
	 * features to the gene id
	 */
	f1 = partition_get(ggs, i);
	f2 = partition_get(ggs, j);
	if (vc->match_all_features || feat->type == CDS) {
	  if (!BAT_Feature_cmp(f1, f2)) {
	    partition_link(ggs, partition_find(ggs, i), partition_find(ggs, j));
	  }
	}
      }
    }
  }

  /*
   * Now simply reassign the geneIDs of the features to the canonical
   * elements of each partition set
   */
  free_feats =
    darray_new(n_fs, darray_ops_copy(&BAT_Owner_Ops[Free_Feature_Allocator]));

  for (i = 0; i < n_fs; i++) {
    feat = (BAT_Feature *) partition_get(ggs, partition_find(ggs, i));
    ffeat = BAT_Free_Feature_new();

    BAT_Free_Feature_set_all(ffeat, feat->type, feat->start, feat->end,
			     feat->score, feat->frame,
			     BAT_Feature_get_strand(feat),
			     BAT_Feature_get_seqname(feat),
			     BAT_Feature_get_source(feat));
    BAT_Free_Feature_set_comment(ffeat, BAT_Comment_copy(feat->comment));
    BAT_Free_Feature_set_gene_id(ffeat, BAT_Feature_get_gene_id(feat));
    BAT_Free_Feature_set_transcript_id(ffeat,
				       BAT_Feature_get_transcript_id(feat));
    darray_add(free_feats, ffeat);
  }

  /* now martial the BAT_GeneTab into a new one */
  b = BAT_Builder_new();
  ffeats = (BAT_Free_Feature **) darray_release(free_feats, &n_fs, 1);
  BAT_Builder_add_features(b, ffeats, n_fs);
  gt = BAT_Builder_build_gene_table(b);

  BAT_Builder_delete(b, 1);
  partition_delete(ggs, 1);

  return gt;
}
